﻿/*
	引用折叠和完美转发    重要
	模板类型个数是固定的

可变参数模板
	基本语法和原理
		1.C++ 11 支持可变参数模板，也就是说支持可变数量参数的函数模版和类模板，可变数目的参数被称为参数包
		  存在两种参数包：模板参数包，表示零或多个模板参数；
						  函数参数包：表示零或多个函数参数
		
		template <class ...Args> void Func(Args... args) {}           传值传参
	    template <class ...Args> void Func(Args&... args) {}          左值引用
	    template <class ...Args> void Func(Args&&... args) {}         万能引用（包含折叠）    传左值就是左值引用，右值就是右值引用
	注：这里的Args  可以替换   Args是参数的意思          ...args是类型

	   2.用省略号来指出一个模板参数或函数参数的表示一个包，在模板参数列表中，class...或
		typename...指出接下来的参数表示零或多个类型列表；在函数参数列表中，类型名后面跟...指出
		接下来表示零或多个形参对象列表；函数参数包可以用左值引用或右值引用表式，跟前面普通模板
		一样，每个参数实例化时遵循引用折叠规则。

	   3.可变参数模板的原理跟模板类似，本质还是去实例化对应类型和个数的多个函数。
	   4.可以使⽤sizeof... 运算符去计算参数包中参数的个数

总结：函数模板，一个函数模板实例化出多个不同类型参数的函数
	  可变参数模板，一个可变参数模板实例化出多个不同参数个数的模板函数



包扩展
	1.对于一个参数包，我们除了能计算他的参数个数，我们能做的唯一的事情就是扩展它，当扩展一个
	  包时，我们还要提供用于每个扩展元素的模式，扩展一个包就是将它分解为构成的元素，对每个元
	  素应用模式，获得扩展后的列表。我们通过在模式的右边放一个省略号(...)来触发扩展操作。
	2.C++还支持更复杂的包扩展，直接将参数包依次展开依次作为实参给一个函数去处理。


emplace_back
	如果传的是左值     就和 push_back 没有对应的区别   都一样   走拷贝构造
	如果传的是右值     跟push_back    一样    走移动构造
	
	如果是这样的直接传参   
	lt.emplace_back("1111");     直接构造
	lt.push_back("1111");        隐式类型转换   构造+移动构造

注意：浅拷贝类型不支持移动构造    也就是说   正常有资源的话就会快一点   浅拷贝的类型也就会更快一些
	  深拷贝会少一个移动构造


	 list<pair<bit::string,int>> lt1;
	  
跟push_back  一样
pair + 拷贝/移动构造pair到list的节点中data上
	 pair<bit::string,int> kv("苹果",1);
	 lt.emplace_back(kv);

跟push_back 一样
	 lt1.emplace_back(move(kv));



这里达到的效果是 push_back 做不到
	lt.emplace_back("苹果"，1);      他不是隐式类型转换    不能写成下面的形式传参    他是可变模板参数   相当于一直传参数包
	相当于实例化出了好多 emplate_back        是构造

	lt.push_back({"苹果",1});        构造 + 移动构造  （隐式类型转换）

总结：emplace 系列 兼容push系列和insert的功能
	  部分场景下emplace 可以直接构造，push和insert是 构造 + 移动构造 或 构造 + 拷贝构造
	  综合下来，emplace更好用 更强大       
	  所以    后续推荐使用  emplace系列代替 push 和 insert


lambda
1.lambda 表达式本质是一个匿名函数对象，跟普通函数不同的是他可以定义在函数内部。
  lambda 表达式语法使用层而言没有类型，所以我们一般是用auto或者模板参数定义的对象去接
  收 lambda 对象。
													返回值类型（一般省略）		
2.lambda表达式的格式： [capture-list] (parameters)-> return type { function boby }
					      捕捉列表		参数列表                       函数体
3.[capture-list] :捕捉列表，该列表总是出现在 lambda 函数的开始位置，编译器根据[]来
  判断接下来的代码是否为 lambda 函数，捕捉列表能够捕捉上下文中的变量供 lambda 函数使
  用，捕捉列表可以传值和传引用捕捉。捕捉列表为空也不能省略。

4.(parameters) ：参数列表，与普通函数的参数列表功能类似，如果不需要参数传递，则可以连
  同()⼀起省略

5.->return type ：返回值类型，用追踪返回类型形式声明函数的返回值类型，没有返回值时此
  部分可省略。一般返回值类型明确情况下，也可省略，由编译器对返回类型进行推导。

6.{function boby} ：函数体，函数体内的实现跟普通函数完全类似，在该函数体内，除了可以
  使用其参数外，还可以使用所有捕获到的变量，函数体为空也不能省略。



捕捉列表
	1.lambda 表达式中默认只能用 lambda 函数体和参数中的变量，如果想用外层作用域中的变量就
	  需要进行捕捉
	2.第一种捕捉方式是在捕捉列表中显示的传值捕捉和传引用捕捉，捕捉的多个变量用逗号分割。[x，
	  y，&z]表示x和y值捕捉，z引用捕捉。
	3.第二种捕捉方式是在捕捉列表中隐式捕捉，我们在捕捉列表写一个 = 表示隐式值捕捉，在捕捉列表
	  写一个 &表示隐式引用捕捉，这样我们 lambda 表达式中用了那些变量，编译器就会自动捕捉那些
	  变量。
	4.第三种捕捉方式是在捕捉列表中混合使用隐式捕捉和显示捕捉。[=,&x]表示其他变量隐式值捕捉，
  	  x引用捕捉；[&,x,y]表示其他变量引用捕捉，x和y值捕捉。当使用混合捕捉时，第一个元素必须是
	  &或=，并且&混合捕捉时，后面的捕捉变量必须是值捕捉，同理 = 混合捕捉时，后面的捕捉变量必
 	  须是引用捕捉。
	5.lambda 表达式如果在函数局部域中，他可以捕捉 lambda 位置之前定义的变量，不能捕捉静态
 	  局部变量和全局变量，静态局部变量和全局变量也不需要捕捉， lambda 表达式中可以直接使
	  用。这也意味着 lambda 表达式如果定义在全局位置，捕捉列表必须为空。
	6.默认情况下， lambda 捕捉列表是被const修饰的，也就是说传值捕捉的过来的对象不能修改，mutable加在参数列表后
	  可以取消其常量性，也就说使用该修饰符后，传值捕捉的对象就可以修改了，但是修改还是形参对象，不会影响实参。
	  使用该修饰符后，参数列表不可省略(即使参数为空)。

lambda可以写到全局   但是一般不会写到全局
局部的静态和全局变量不能捕捉    也不需要进行捕捉



lambda 原理
	1.lambda 的原理和范围for很像，编译后从汇编指令层的角度看，压根就没有 lambda 和范围for
	  这样的东西。范围for底层是迭代器，然而lambda底层是仿函数对象，也就说我们写了⼀个
	  lambda 以后，编译器会生成一个对应的仿函数的类。

	  编译器生成的对应的仿函数   仿函数类的名称是编译器自己生成的   每一次是不一样的 UUID

	2.仿函数的类名是编译的时候按一定规则生成的，保证不同的 lambda 生成的类名不同，lambda参数/返
	  回类型/函数体  就是  仿函数operator()的参数/返回类型/函数体， lambda 的捕捉列表本质是生成
	  的仿函数  类的成员变量(捕捉的值就会传来初始化这个对象)，也就是说捕捉列表的变量都是 lambda 类
	  构造函数的实参，当然隐式捕捉，编译器要看使用哪些就传哪些对象。



默认的移动构造和移动赋值
	1.原来C++类中，有6个默认成员函数：构造函数/析构函数/拷⻉构造函数/拷⻉赋值重载/取地址重
	  载/const 取地址重载，最后重要的是前4个，后两个用处不大，默认成员函数就是我们不写编译器
	  会生成一个默认的。C++11新增了两个默认成员函数，移动构造函数和移动赋值运算符重载。
	
	2.如果你没有自己实现移动构造函数，且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意一            条件更多
	  个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数，对于内置类型成员会执
	  行逐成员按字节拷贝，自定义类型成员，则需要看这个成员是否实现移动构造，如果实现了就调用
	  移动构造，没有实现就调用拷贝构造。
	
	3.如果你没有自己实现移动赋值重载函数，且没有实现析构函数、拷贝构造、拷贝赋值重载中的任意
	  一个，那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数，对于内置类型成员会
	  执行逐成员按字节拷贝，自定义类型成员，则需要看这个成员是否实现移动赋值，如果实现了就调
	  用移动赋值，没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
	
	4.如果你提供了移动构造或者移动赋值，编译器不会自动提供拷贝构造和拷贝赋值。



成员变量声明时给缺省值
	成员变量声明时给缺省值是给初始化列表用的，如果没有显示在初始化列表初始化，就会在初始化列
	表用这个缺省值初始化，如果   也就是在成员列表给值   是给初始化列表的  但是初始化列表中显示
	的写了初始化，那就不会用对应的缺省值   如果没写，就会使用对应的缺省值



defult和delete
	1.C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数，但是因为一些原因
	  这个函数没有默认生成。比如：提供了拷贝构造，就不会生成移动构造了，那么可以使用default关键字
	  显示指定移动构造生成。

	2.如果能想要限制某些默认函数的生成，在C++98中，是该函数设置成private，并且只声明不实现，
	  这样只要其他人想要调用就会报错。在C++11中更简单，只需在该函数声明加上=delete即可，该语
	  法指示编译器不生成对应函数的默认版本，称 =delete修饰的函数为删除函数。   
	  防止拷贝：譬如IO流中



final 和 override
	final       修饰类不能继承   
	override    放在派生类重写的虚函数去检查是否完成重写   如果没有完成重写就会报错



STL中的一些变化									        * 哈希表		
	添加了一些新容器   array   forward_list   unordered_map   unordered_set
																 *   
	STL中容器的新接口也不少，最重要的就是右值引用和移动语义相关的push/insert/emplace系列
	接口和移动构造和移动赋值，还有initializer_list版本的构造等，还有一些不是很重要的 
	cbegin/cend等需要时查文档。



包装器（本质也是仿函数）
	function
	1.std::function 是一个类模板，也是一个包装器。  std::function 的实例对象可以包装存
	  储其他的可以调用对象，包括函数指针、仿函数、 lambda 、 bind 表达式等，存储的可调用对
	  象被称为 std::function 的目标。若 std::function 不含目标，则称它为空。调用空std::function 
	  的目标导致抛出 std::bad_function_call 异常。

	2.以上是 function 的原型，他被定义<functional>头文件中。
	
	3.函数指针、仿函数、 lambda 等可调用对象的类型各不相同， std::function 的优势就是统一类型，
	  对他们都可以进行包装，这样在很多地方就方便声明可调用对象的类型

	*  重要：包装成员函数，一定要传对象或者对象的指针



bind  绑定
	1.bind 是一个函数模板，它也是一个可调用对象的包装器，可以把他看做一个函数适配器，对接收
	  的fn可调用对象进行处理后返回一个可调用对象。 bind  可以用来调整 参数个数 和 参数顺序。
	  bind 也在<functional>这个头文件中。
	
	2.调用bind的一般形式： auto newCallable = bind(callable,arg_list); 其中newCallable本身是⼀个可调用对象，
	  arg_list是一个逗号分隔的参数列表，对应给定的callable的参数。当我们调用newCallable时，newCallable会
	  调用callable，并传给它arg_list中的参数。

	3.arg_list 中的参数可能包含形如_n的名字，其中n是一个整数，这些参数是占位符，表示newCallable的参数，
	  它们占据了传递给newCallable的参数的位置。数值n表示生成的可调用对象中参数的位置：_1 为newCallable
	  的第一个参数，_2 为第二个参数，以此类推。_1/_2/_3....这些占位符放到placeholders的一个命名空间中。

*/